﻿unit EnhancedServerModule;

interface

uses
  System.SysUtils, System.Generics.Collections, System.Rtti, System.TypInfo,
  Sparkle.HttpServer.Module, Sparkle.HttpServer.Context, Sparkle.Logger,
  System.DateUtils, ServerLogging, AuthMiddleware, ServerModuleManager;

type
  TRouteAttribute = class(TCustomAttribute)
  private
    FPath: string;
    FMethod: string;
  public
    constructor Create(const AMethod: string = 'GET'; const APath: string = '');
    property Method: string read FMethod;
    property Path: string read FPath;
  end;

  TRouteInfo = record
    MethodName: string;
    HttpMethod: string;
    Path: string;
    RttiMethod: TRttiMethod;
    constructor Create(AMethodName, AHttpMethod, APath: string; ARttiMethod: TRttiMethod);
  end;

  TRequestIDMiddleware = class(THttpServerMiddleware)
  protected
    procedure ProcessRequest(Context: THttpServerContext; Next: THttpServerProc); override;
  end;

  TRequestID = class
  private
    FID: string;
  public
    constructor Create(const AID: string);
    property ID: string read FID;
  end;

  TEnhancedServerModule = class(THttpServerModule, IModuleLifecycle)
  private
    FRequestLogger: IRequestLogger;
    FAuthMiddleware: TAuthenticationMiddleware;
    FRequestIDMiddleware: TRequestIDMiddleware;
    FRouteMap: TDictionary<string, TRouteInfo>;
    FRttiContext: TRttiContext;

    function GenerateRequestID: string;
    procedure SetupMiddlewares;
    function ExtractRequestID(Context: THttpServerContext): string;
    procedure InitializeRoutes;
    function FindRouteHandler(Context: THttpServerContext; out RouteInfo: TRouteInfo): Boolean;
    function GetRelativePath(Context: THttpServerContext): string;
    function NormalizePath(const Path: string): string;
    function PathMatchesRoute(const RequestPath, RoutePath: string): Boolean;

    // 新增方法：确保 RequestID 一致性
    procedure EnsureRequestIDConsistency(Context: THttpServerContext);
  protected
    procedure Initialize; virtual;
    procedure ProcessRequest(const Context: THttpServerContext); override;

    // 日志方法
    procedure LogDebug(const Msg: string); virtual;
    procedure LogInfo(const Msg: string); virtual;
    procedure LogWarning(const Msg: string); virtual;
    procedure LogError(const Msg: string); virtual;
    procedure LogFatal(const Msg: string); virtual;

    // 带上下文的日志方法
    procedure LogDebugContext(Context: THttpServerContext; const Msg: string); virtual;
    procedure LogInfoContext(Context: THttpServerContext; const Msg: string); virtual;
    procedure LogWarningContext(Context: THttpServerContext; const Msg: string); virtual;
    procedure LogErrorContext(Context: THttpServerContext; const Msg: string); virtual;

    // 性能日志
    procedure LogPerformance(const Operation: string; Milliseconds: Int64); virtual;

    function GetRequestLogger: IRequestLogger;
  public
    constructor Create(const ABaseUri: string); override;
    destructor Destroy; override;

    // IModuleLifecycle
    procedure Start;
    procedure Stop;
    function GetModuleName: string; virtual;

    // 基础路由示例（可选）
    [TRoute('GET', '/health')]
    procedure HealthCheck(Context: THttpServerContext);

    [TRoute('GET', '/info')]
    procedure GetInfo(Context: THttpServerContext);
  end;

implementation

{ TRouteAttribute }

constructor TRouteAttribute.Create(const AMethod, APath: string);
begin
  FMethod := AMethod;
  FPath := APath;
end;

{ TRouteInfo }

constructor TRouteInfo.Create(AMethodName, AHttpMethod, APath: string; ARttiMethod: TRttiMethod);
begin
  MethodName := AMethodName;
  HttpMethod := AHttpMethod;
  Path := APath;
  RttiMethod := ARttiMethod;
end;

{ TRequestID }

constructor TRequestID.Create(const AID: string);
begin
  inherited Create;
  FID := AID;
end;

{ TRequestIDMiddleware }

procedure TRequestIDMiddleware.ProcessRequest(Context: THttpServerContext;
  Next: THttpServerProc);
var
  RequestID: string;
  ExistingRequestID: TRequestID;
begin
  // 首先检查是否已经存在 RequestID
  ExistingRequestID := Context.Item<TRequestID>;
  if ExistingRequestID <> nil then
  begin
    // 如果已经存在，重用这个 ID
    RequestID := ExistingRequestID.ID;
  end
  else
  begin
    // 从 Header 中获取或生成新的 RequestID
    RequestID := Context.Request.Headers.Get('X-Request-ID');
    if RequestID = '' then
      RequestID := TGUID.NewGuid.ToString.Replace('{', '').Replace('}', '');

    // 创建并设置 RequestID 对象
    Context.SetItem<TRequestID>(TRequestID.Create(RequestID));
  end;

  // 确保响应头中也设置相同的 RequestID
  Context.Response.Headers.SetValue('X-Request-ID', RequestID);

  // 记录调试信息
  // TServerModuleManager.Instance.Logger.Info(Format('RequestID Middleware: %s', [RequestID]));

  Next(Context);
end;

{ TEnhancedServerModule }

constructor TEnhancedServerModule.Create(const ABaseUri: string);
begin
  inherited Create(ABaseUri);

  FRequestLogger := TServerModuleManager.Instance.Logger;
  FRouteMap := TDictionary<string, TRouteInfo>.Create;
  FRttiContext := TRttiContext.Create;

  Initialize;
  InitializeRoutes;
  SetupMiddlewares;

  FRequestLogger.Info(Format('Module %s created with base URI: %s', [GetModuleName, ABaseUri]));
end;

destructor TEnhancedServerModule.Destroy;
begin
  // 中间件由父类的接口列表管理，不需要手动释放
  // FAuthMiddleware.Free;
  // FRequestIDMiddleware.Free;
  // 中间件由父类管理，不需要手动释放
  // 确保 RTTI 和路由映射正确清理
  if Assigned(FRouteMap) then
    FRouteMap.Free;

  FRequestLogger.Info(Format('Module %s destroyed', [GetModuleName]));
  inherited;
end;

procedure TEnhancedServerModule.Initialize;
begin
  FRequestLogger.Debug(Format('Module %s initializing', [GetModuleName]));
end;

procedure TEnhancedServerModule.EnsureRequestIDConsistency(Context: THttpServerContext);
var
  RequestIDObj: TRequestID;
  HeaderRequestID: string;
begin
  RequestIDObj := Context.Item<TRequestID>;
  if RequestIDObj <> nil then
  begin
    // 确保响应头中的 RequestID 与 Context 中的一致
    HeaderRequestID := Context.Response.Headers.Get('X-Request-ID');
    if HeaderRequestID <> RequestIDObj.ID then
    begin
      Context.Response.Headers.SetValue('X-Request-ID', RequestIDObj.ID);
      LogDebugContext(Context,
        Format('Fixed RequestID consistency: Context=%s, Header=%s, Now=%s',
          [RequestIDObj.ID, HeaderRequestID, RequestIDObj.ID]));
    end;
  end;
end;

procedure TEnhancedServerModule.InitializeRoutes;
var
  RttiType: TRttiType;
  Methods: TArray<TRttiMethod>;
  Method: TRttiMethod;
  Attribute: TCustomAttribute;
  RouteAttr: TRouteAttribute;
  RouteKey: string;
  RouteInfo: TRouteInfo;
  NormalizedPath: string;
begin
  FRequestLogger.Debug(Format('Initializing routes for module %s', [GetModuleName]));

  RttiType := FRttiContext.GetType(ClassType);
  Methods := RttiType.GetMethods;

  for Method in Methods do
  begin
    for Attribute in Method.GetAttributes do
    begin
      if Attribute is TRouteAttribute then
      begin
        RouteAttr := TRouteAttribute(Attribute);

        // 规范化路径
        NormalizedPath := NormalizePath(RouteAttr.Path);

        // 构建路由键：HTTP方法 + 规范化路径
        RouteKey := UpperCase(RouteAttr.Method) + ':' + NormalizedPath;

        RouteInfo := TRouteInfo.Create(
          Method.Name,
          RouteAttr.Method,
          NormalizedPath,
          Method
        );

        FRouteMap.AddOrSetValue(RouteKey, RouteInfo);

        FRequestLogger.Debug(Format('Registered route: %s %s -> %s.%s',
          [RouteAttr.Method, NormalizedPath, GetModuleName, Method.Name]));
      end;
    end;
  end;

  FRequestLogger.Info(Format('Module %s registered %d routes', [GetModuleName, FRouteMap.Count]));
end;

function TEnhancedServerModule.NormalizePath(const Path: string): string;
begin
  Result := Path.Trim;

  // 确保路径以 / 开头
  if (Result <> '') and (not Result.StartsWith('/')) then
    Result := '/' + Result;

  // 空路径转为根路径
  if Result = '' then
    Result := '/';
end;

function TEnhancedServerModule.GetRelativePath(Context: THttpServerContext): string;
var
  RequestPath: string;
  BasePath: string;
  NormalizedBasePath: string;
begin
  RequestPath := Context.Request.Uri.Path;
  BasePath := BaseUri.Path;

  // 规范化基路径
  NormalizedBasePath := NormalizePath(BasePath);

  // 记录调试信息
  LogDebugContext(Context, Format('BasePath: "%s", RequestPath: "%s"', [NormalizedBasePath, RequestPath]));

  // 如果请求路径正好等于基路径，返回根路径
  if RequestPath = NormalizedBasePath then
  begin
    Result := '/';
    Exit;
  end;

  // 如果请求路径以基路径开头，提取相对路径
  if RequestPath.StartsWith(NormalizedBasePath) then
  begin
    Result := Copy(RequestPath, Length(NormalizedBasePath) + 1, MaxInt);

    // 如果提取后为空，说明请求路径比基路径多一个斜杠
    if Result = '' then
      Result := '/'
    else if not Result.StartsWith('/') then
      Result := '/' + Result;
  end
  else
  begin
    // 如果请求路径不以基路径开头，可能是路径配置问题
    Result := RequestPath;
  end;

  // 最终规范化相对路径
  Result := NormalizePath(Result);

  LogDebugContext(Context, Format('RelativePath: "%s"', [Result]));
end;

function TEnhancedServerModule.PathMatchesRoute(const RequestPath, RoutePath: string): Boolean;
begin
  // 精确匹配
  if RequestPath = RoutePath then
    Exit(True);

  // 处理根路径的特殊情况
  if (RequestPath = '/') and (RoutePath = '') then
    Exit(True);

  if (RequestPath = '') and (RoutePath = '/') then
    Exit(True);

  Result := False;
end;

function TEnhancedServerModule.FindRouteHandler(Context: THttpServerContext;
  out RouteInfo: TRouteInfo): Boolean;
var
  RelativePath: string;
  RouteKey: string;
  RoutePair: TPair<string, TRouteInfo>;
  NormalizedRequestPath: string;
begin
  Result := False;
  RelativePath := GetRelativePath(Context);
  NormalizedRequestPath := NormalizePath(RelativePath);

  // 构建路由查找键
  RouteKey := UpperCase(Context.Request.Method) + ':' + NormalizedRequestPath;

  LogDebugContext(Context, Format('Looking for route: %s', [RouteKey]));

  // 精确匹配
  if FRouteMap.TryGetValue(RouteKey, RouteInfo) then
  begin
    Result := True;
    LogDebugContext(Context, Format('Exact route match found: %s', [RouteInfo.MethodName]));
    Exit;
  end;

  // 如果没有精确匹配，尝试路径匹配（处理 /path 和 /path/ 的等价性）
  for RoutePair in FRouteMap do
  begin
    if SameText(Copy(RoutePair.Key, 1, Pos(':', RoutePair.Key) - 1), Context.Request.Method) then
    begin
      if PathMatchesRoute(NormalizedRequestPath, RoutePair.Value.Path) then
      begin
        RouteInfo := RoutePair.Value;
        Result := True;
        LogDebugContext(Context, Format('Path match found: %s -> %s',
          [NormalizedRequestPath, RouteInfo.MethodName]));
        Exit;
      end;
    end;
  end;

  LogDebugContext(Context, Format('No route handler found for: %s %s',
    [Context.Request.Method, NormalizedRequestPath]));
end;

procedure TEnhancedServerModule.ProcessRequest(const Context: THttpServerContext);
var
  StartTime: TDateTime;
  RouteInfo: TRouteInfo;
  CurrentRequestID: string;
begin
  StartTime := Now;

  try
    // 在处理请求前确保 RequestID 一致性
    EnsureRequestIDConsistency(Context);

    CurrentRequestID := ExtractRequestID(Context);
    LogInfoContext(Context, Format('Processing request: %s %s (RequestID: %s)',
      [Context.Request.Method, Context.Request.Uri.Path, CurrentRequestID]));

    // 查找匹配的路由处理器
    if FindRouteHandler(Context, RouteInfo) then
    begin
      try
        LogDebugContext(Context, Format('Routing to: %s.%s',
          [GetModuleName, RouteInfo.MethodName]));

        // 在处理前再次确保 RequestID 一致性
        EnsureRequestIDConsistency(Context);

        // 使用RTTI调用目标方法
        RouteInfo.RttiMethod.Invoke(Self, [Context]);

        LogInfoContext(Context, Format('Route handled successfully: %s',
          [RouteInfo.MethodName]));
      except
        on E: Exception do
        begin
          LogErrorContext(Context, Format('Error in route handler %s: %s',
            [RouteInfo.MethodName, E.Message]));
          Context.Response.StatusCode := 500;
          Context.Response.Close(TEncoding.UTF8.GetBytes(
            '{"success": false, "code": 500, "message": "Internal server error: ' + E.Message + '"}'));
        end;
      end;
    end
    else
    begin
      // 没有找到匹配的路由
      LogWarningContext(Context, Format('No route found for: %s %s (relative: %s)',
        [Context.Request.Method, Context.Request.Uri.Path, GetRelativePath(Context)]));
      Context.Response.StatusCode := 404;
      Context.Response.Close(TEncoding.UTF8.GetBytes(
        '{"success": false, "code": 404, "message": "Not found", "details": "No route matches the request"}'));
    end;

  except
    on E: Exception do
    begin
      LogErrorContext(Context, Format('Error processing request: %s', [E.Message]));
      Context.Response.StatusCode := 500;
      Context.Response.Close(TEncoding.UTF8.GetBytes(
        '{"success": false, "code": 500, "message": "Internal server error"}'));
    end;
  end;

  // 记录性能信息
  LogPerformance('ProcessRequest', MilliSecondsBetween(Now, StartTime));
end;

procedure TEnhancedServerModule.SetupMiddlewares;
var
  AuthService: IAuthenticationService;
  ExcludedPaths: TArray<string>;
begin
  FRequestLogger.Debug(Format('Setting up middlewares for %s', [GetModuleName]));

  AuthService := TSimpleAuthenticationService.Create('your-secret-key');
  ExcludedPaths := ['/auth/login', '/health', '/info','/api/user'];

  FAuthMiddleware := TAuthenticationMiddleware.Create(AuthService, ExcludedPaths);
  FRequestIDMiddleware := TRequestIDMiddleware.Create;

  AddMiddleware(FAuthMiddleware);
  AddMiddleware(FRequestIDMiddleware);

  FRequestLogger.Debug(Format('Middlewares setup completed for %s', [GetModuleName]));
end;

function TEnhancedServerModule.GenerateRequestID: string;
begin
  Result := TGUID.NewGuid.ToString.Replace('{', '').Replace('}', '');
end;

function TEnhancedServerModule.ExtractRequestID(Context: THttpServerContext): string;
var
  RequestIDObj: TRequestID;
begin
  RequestIDObj := Context.Item<TRequestID>;
  if RequestIDObj <> nil then
    Result := RequestIDObj.ID
  else
  begin
    // 如果没有找到，生成一个新的并设置到 Context 中
    Result := GenerateRequestID;
    Context.SetItem<TRequestID>(TRequestID.Create(Result));

    // 同时设置到响应头中
    Context.Response.Headers.SetValue('X-Request-ID', Result);

    LogDebugContext(Context, Format('Generated new RequestID: %s', [Result]));
  end;
end;

// 日志方法实现
procedure TEnhancedServerModule.LogDebug(const Msg: string);
begin
  if Assigned(FRequestLogger) then
    FRequestLogger.Debug(Format('[%s] %s', [GetModuleName, Msg]));
end;

procedure TEnhancedServerModule.LogInfo(const Msg: string);
begin
  if Assigned(FRequestLogger) then
    FRequestLogger.Info(Format('[%s] %s', [GetModuleName, Msg]));
end;

procedure TEnhancedServerModule.LogWarning(const Msg: string);
begin
  if Assigned(FRequestLogger) then
    FRequestLogger.Warning(Format('[%s] %s', [GetModuleName, Msg]));
end;

procedure TEnhancedServerModule.LogError(const Msg: string);
begin
  if Assigned(FRequestLogger) then
    FRequestLogger.Error(Format('[%s] %s', [GetModuleName, Msg]));
end;

procedure TEnhancedServerModule.LogFatal(const Msg: string);
begin
  if Assigned(FRequestLogger) then
    FRequestLogger.Fatal(Format('[%s] %s', [GetModuleName, Msg]));
end;

procedure TEnhancedServerModule.LogDebugContext(Context: THttpServerContext; const Msg: string);
var
  RequestID: string;
begin
  RequestID := ExtractRequestID(Context);
  if Assigned(FRequestLogger) then
    FRequestLogger.LogRequest(RequestID, GetModuleName, Msg, llDebug);
end;

procedure TEnhancedServerModule.LogInfoContext(Context: THttpServerContext; const Msg: string);
var
  RequestID: string;
begin
  RequestID := ExtractRequestID(Context);
  if Assigned(FRequestLogger) then
    FRequestLogger.LogRequest(RequestID, GetModuleName, Msg, llInfo);
end;

procedure TEnhancedServerModule.LogWarningContext(Context: THttpServerContext; const Msg: string);
var
  RequestID: string;
begin
  RequestID := ExtractRequestID(Context);
  if Assigned(FRequestLogger) then
    FRequestLogger.LogRequest(RequestID, GetModuleName, Msg, llWarning);
end;

procedure TEnhancedServerModule.LogErrorContext(Context: THttpServerContext; const Msg: string);
var
  RequestID: string;
begin
  RequestID := ExtractRequestID(Context);
  if Assigned(FRequestLogger) then
    FRequestLogger.LogRequest(RequestID, GetModuleName, Msg, llError);
end;

procedure TEnhancedServerModule.LogPerformance(const Operation: string; Milliseconds: Int64);
begin
  if Assigned(FRequestLogger) then
    FRequestLogger.LogPerformance('SYSTEM', Format('%s.%s', [GetModuleName, Operation]), Milliseconds);
end;

function TEnhancedServerModule.GetRequestLogger: IRequestLogger;
begin
  Result := FRequestLogger;
end;

procedure TEnhancedServerModule.Start;
begin
  FRequestLogger.Info(Format('Module %s started', [GetModuleName]));
end;

procedure TEnhancedServerModule.Stop;
begin
  FRequestLogger.Info(Format('Module %s stopped', [GetModuleName]));
end;

function TEnhancedServerModule.GetModuleName: string;
begin
  Result := ClassName;
end;

{ 示例路由处理方法 }

procedure TEnhancedServerModule.HealthCheck(Context: THttpServerContext);
var
  RequestID: string;
begin
  try
    RequestID := ExtractRequestID(Context);
    Context.Response.ContentType := 'application/json';
    Context.Response.Close(TEncoding.UTF8.GetBytes(
      Format('{"status": "healthy", "timestamp": "%s", "module": "%s", "requestId": "%s"}',
        [FormatDateTime('yyyy-mm-dd hh:nn:ss', Now), GetModuleName, RequestID])));
  except
    on E: Exception do
    begin
      Context.Response.StatusCode := 500;
      Context.Response.Close(TEncoding.UTF8.GetBytes('{"error": "Internal server error"}'));
    end;
  end;
end;

procedure TEnhancedServerModule.GetInfo(Context: THttpServerContext);
var
  RequestID: string;
begin
  RequestID := ExtractRequestID(Context);
  Context.Response.ContentType := 'application/json';
  Context.Response.Close(TEncoding.UTF8.GetBytes(
    Format('{"module": "%s", "version": "1.0.0", "routes": %d, "requestId": "%s"}',
      [GetModuleName, FRouteMap.Count, RequestID])));
end;

end.
